﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Driver;
using Sustainalytics.Entities;
using Sustainalytics.Entities.Governance;
using Sustainalytics.Utils;
using System.Configuration.Abstractions;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;

namespace Sustainalytics.Governance.Computing
{
    public class GovernanceDataManager
    {
        private readonly IMongoCollection<GovernanceCompanyProfile> _governanceCompanyProfileCollection;
        private readonly IMongoCollection<GovernanceIndicatorTemplate> _governanceIndicatorTemplateCollection;
        private readonly IMongoCollection<GovernanceIndicator> _governanceIndicatorCollection;
        private readonly IMongoCollection<GovernanceComputationResult> _governanceComputationResultCollection;
        private readonly IMongoCollection<GovernanceAssessmentThreshold> _governanceAssesmentThresholdCollection;
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;
        private readonly IMongoCollection<CompanyProfileResearchState> _companyProfileResearchStateCollection;
        private readonly IMongoCollection<GovernanceIssue> _governanceIssueCollection;
        private readonly IMongoCollection<GovernanceSource> _governanceSourceCollection;
        private readonly IMongoCollection<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> _corporateGovernanceResearchProfileCollection;

        public List<GovernanceCompanyProfile> CompanyProfiles { get; set; }
        public List<GovernanceIndicatorTemplate> IssueTemplates { get; set; }
        public Dictionary<Guid, Dictionary<string, int>> Indicators { get; set; }
        public Dictionary<string, Dictionary<string, float>> RegionIssueWeights { get; set; }
        public Dictionary<string, Dictionary<string, float>> RegionIndicatorWeights { get; set; }
        public Dictionary<string, Dictionary<string, float>> CorrectionFactors { get; set; }
        public List<string> ImportedIssues { get; set; }
        public Dictionary<Guid, Dictionary<string, GovernanceIssuePerformance>> ImportedIssuePerformances { get; set; }
        public Dictionary<string, List<int>> AssesmentThresholds { get; set; }
        public Dictionary<Guid, GovernanceComputationResult> ImportedComputationResults { get; set; }

        private ConcurrentBag<string> _log;
        public List<string> Log { get { return _log.ToList(); } }


        public GovernanceDataManager(IMongoCollection<GovernanceCompanyProfile> governanceCompanyProfileCollection,
            IMongoCollection<GovernanceIndicatorTemplate> governanceIndicatorTemplateCollection,
            IMongoCollection<GovernanceIndicator> governanceIndicatorCollection,
            IMongoCollection<GovernanceComputationResult> governanceComputationResultCollection,
            IMongoCollection<GovernanceAssessmentThreshold> governanceAssesmentThresholdCollection,
            IMongoCollection<CorporateData> corporateDataCollection,
            IMongoCollection<CompanyProfileResearchState> companyProfileResearchStateCollection,
            IMongoCollection<GovernanceIssue> governanceIssueCollection,
            IMongoCollection<GovernanceSource> governanceSourceCollection,
            IMongoCollection<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> corporateGovernanceResearchProfileCollection
            )
        {
            _governanceCompanyProfileCollection = governanceCompanyProfileCollection;
            _governanceIndicatorTemplateCollection = governanceIndicatorTemplateCollection;
            _governanceIndicatorCollection = governanceIndicatorCollection;
            _governanceComputationResultCollection = governanceComputationResultCollection;
            _governanceAssesmentThresholdCollection = governanceAssesmentThresholdCollection;
            _corporateDataCollection = corporateDataCollection;
            _companyProfileResearchStateCollection = companyProfileResearchStateCollection;
            _governanceIssueCollection = governanceIssueCollection;
            _governanceSourceCollection = governanceSourceCollection;
            _corporateGovernanceResearchProfileCollection = corporateGovernanceResearchProfileCollection;

            var issues = ConfigurationManager.Instance.AppSettings["ImportIssues"];

            ImportedIssues = issues.Split(',').ToList();

            _log = new ConcurrentBag<string>();
        }


        public void GetData()
        {
            LoadCompanyProfiles();
            var companyIds = CompanyProfiles.Select(c => c.Id);

            new Action[]
             {
                () => LoadIndicatorTemplates(),
                () => LoadIndicators(companyIds),
                () => LoadIssuePerformances(companyIds),
                () => LoadAssesmentThresholds()
             }
             .RunInParallel();

            var result = _log.ToList();
        }


        public void LoadComputationResults()
        {
            var sw = Stopwatch.StartNew();


            LoadCompanyProfiles();

            var companyIds = CompanyProfiles.Select(c => c.Id);


            var computationResults = _corporateGovernanceResearchProfileCollection
                .Find(x => companyIds.Contains(x.Id))
                .ToEnumerable()
                .Select(x => new GovernanceComputationResult
                {
                    Id = x.Id,
                    OverallPerformance = new GovernanceOverallPerformance()
                    {
                       Assessment = (AssessmentType)x.GovernanceComputationResult.OverallPerformance.Assessment.Value,
                       Score = x.GovernanceComputationResult.OverallPerformance.Score.Value,
                       Weight = 1,
                       WeightedScore = x.GovernanceComputationResult.OverallPerformance.Score.Value,
                       Performances = null
                    },
                    RelativePerformance = null,
                    IssuesPerformance = x.GovernanceComputationResult.IssuesPerformance.Select(issue => new GovernanceIssuePerformance()
                    {
                        Name = issue.Name,
                        Number = issue.Number,
                        Assessment = (AssessmentType)issue.Assessment.Value,
                        Score = issue.Score.Value,
                        Weight = issue.Weight.Value,
                        WeightedScore = issue.WeightedScore.Value,
                        Performances = null
                    }).ToList()
                })
                .ToList();

            ImportedComputationResults = computationResults.ToDictionary(r => r.Id);

            _log.Add($"ComputationResults: {sw.Elapsed}");
        }

        private void LoadCompanyProfiles()
        {
            CompanyProfiles = _governanceCompanyProfileCollection.Find(new BsonDocument())
                .Project(x => new GovernanceCompanyProfile() { Id = x.Id, CompanyName = x.CompanyName, Industry = x.Industry, RegionCode = x.RegionCode, MarketCapitalization = x.MarketCapitalization })
                .ToList();
        }

        public void LoadIndicatorTemplates()
        {
            var sw = Stopwatch.StartNew();

            IssueTemplates = _governanceIndicatorTemplateCollection
                .Find(new BsonDocument())
                .Project(x => new GovernanceIndicatorTemplate() { IssueName = x.IssueName, IssueNumber = x.IssueNumber })
                .ToEnumerable()
                .GroupBy(x => new { x.IssueNumber, x.IssueName })
                .Select(x => new GovernanceIndicatorTemplate() { IssueName = x.Key.IssueName, IssueNumber = x.Key.IssueNumber })
                .OrderBy(x => x.IssueNumber)
                .ToList();

            sw.Stop();
            _log.Add($"IndicatorTemplates: {sw.Elapsed}");
        }

        private bool IsIndicator(string number)
        {
            return number.Split('.').Length > 2;
        }

        private void LoadIndicators(IEnumerable<Guid> companyIds)
        {
            var sw = Stopwatch.StartNew();

            var indicators = _governanceIndicatorCollection.Find(x => companyIds.Contains(x.CompanyId))
                .Project(x => new GovernanceIndicator() { CompanyId = x.CompanyId, Number = x.Number, Score = x.Score })
                .ToList();

            Indicators = indicators.Where(x => IsIndicator(x.Number))
                .OrderBy(x => x.Number)
                .GroupBy(x => x.CompanyId)
                .ToDictionary(k => k.Key, v => v.ToDictionary(x => x.Number, y => y.Score));

            sw.Stop();
            _log.Add($"Indicators: {sw.Elapsed}");
        }

        private void LoadIssuePerformances(IEnumerable<Guid> companyIds)
        {
            var sw = Stopwatch.StartNew();

            var computationResults = _governanceComputationResultCollection
                .Find(x => companyIds.Contains(x.Id))
                .Project(x => new GovernanceComputationResult() { Id = x.Id, IssuesPerformance = x.IssuesPerformance })
                .ToList();

            ImportedIssuePerformances = computationResults
                .ToDictionary(r => r.Id, r => r.IssuesPerformance.Where(i => ImportedIssues.Contains(i.Number))
                .ToDictionary(k => k.Number));

            sw.Stop();

            _log.Add($"IssuePerformances: {sw.Elapsed}");
        }

        public void SaveComputationResults(IEnumerable<GovernanceComputationResult> documents)
        {
            var sw = Stopwatch.StartNew();

            _governanceComputationResultCollection.Database.DropCollection(typeof(GovernanceComputationResult).Name);

            _governanceComputationResultCollection.InsertMany(documents);

            _governanceComputationResultCollection.Indexes.CreateOne(Builders<GovernanceComputationResult>.IndexKeys.Ascending(x => x.OverallPerformance.Assessment));

            sw.Stop();

            _log.Add($"Save Computation Results: {sw.Elapsed}");
        }

        private void LoadAssesmentThresholds()
        {
            var sw = Stopwatch.StartNew();

 
            AssesmentThresholds = _governanceAssesmentThresholdCollection.Find(new BsonDocument()).ToList()
                .ToDictionary(x => x.Number, x => x.Thresholds);

            sw.Stop();
            _log.Add($"AssesmentThresholds: {sw.Elapsed}");
        }


        public void UpdateGovernanceResearchFlagInCorporateData(List<Guid> companyIds)
        {
            _companyProfileResearchStateCollection.UpdateMany(cp => companyIds.Contains(cp.Id),
                Builders<CompanyProfileResearchState>.Update.Set(x => x.CorporateGovernanceResearchState.ResearchState, ResearchState.ResearchInProgress));

            _corporateDataCollection.UpdateMany(cp => companyIds.Contains(cp.Id), 
                Builders<CorporateData>.Update.Set(x => x.CorporateGovernanceResearchState.ResearchState, ResearchState.ResearchInProgress));
        }

        public void CleanupRawData(List<Guid> companyIds)
        {
            var result = _governanceCompanyProfileCollection.DeleteMany(cp => companyIds.Contains(cp.Id));
            _log.Add($"{result.DeletedCount} deleted GovernanceCompanyProfile entities");

            result = _governanceIndicatorCollection.DeleteMany(cp => companyIds.Contains(cp.CompanyId));
            _log.Add($"{result.DeletedCount} deleted GovernanceIndicator entities");

            result = _governanceIssueCollection.DeleteMany(cp => companyIds.Contains(cp.CompanyId));
            _log.Add($"{result.DeletedCount} deleted GovernanceIssue entities");

            result = _governanceSourceCollection.DeleteMany(cp => companyIds.Contains(cp.CompanyId));
            _log.Add($"{result.DeletedCount} deleted GovernanceSource entities");

        }
    }

}
